package com.laf.common.utils;

import cn.hutool.core.codec.Base64;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.enums.ErrorInfo;
import com.arcsoft.face.enums.ImageFormat;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import com.laf.common.exception.ServiceException;
import com.laf.common.properties.FaceEngineProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 人脸识别工具类
 */
@Slf4j
@Import(FaceEngineProperties.class)
@Component
@RequiredArgsConstructor
public class FaceUtils {

    private final FaceEngineProperties faceEngineProperties;
    public FaceEngine faceEngine;

    /**
     * 初始化人脸验证引擎
     */
    @PostConstruct
    public void init() {
        // 创建人脸引擎实例
        FaceEngine faceEngine = new FaceEngine(faceEngineProperties.getLibPath());

        // 激活引擎
        int activeCode = faceEngine.activeOnline(faceEngineProperties.getAppid(), faceEngineProperties.getSdkKey());
        if (activeCode != ErrorInfo.MOK.getValue() && activeCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
            log.error("引擎激活失败 : {} ", activeCode);
            throw new RuntimeException("引擎激活失败");
        }

        // 获取引擎配置
        EngineConfiguration engineConfiguration = getEngineConfiguration();

        // 初始化引擎
        int initCode = faceEngine.init(engineConfiguration);
        if (initCode != ErrorInfo.MOK.getValue()) {
            log.error("初始化引擎出错!");
            throw new RuntimeException("初始化引擎出错!");
        }

        this.faceEngine = faceEngine;
    }

    /**
     * 获取引擎配置
     *
     * @return 引擎配置对象
     */
    private static EngineConfiguration getEngineConfiguration() {
        EngineConfiguration engineConfiguration = new EngineConfiguration();
        // 设置检测模式为单张图像
        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
        // 设置人脸检测角度为全角度
        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);

        // 配置功能
        FunctionConfiguration functionConfiguration = new FunctionConfiguration();
        functionConfiguration.setSupportAge(true);
        functionConfiguration.setSupportFace3dAngle(true);
        functionConfiguration.setSupportFaceDetect(true);
        functionConfiguration.setSupportFaceRecognition(true);
        functionConfiguration.setSupportGender(true);
        functionConfiguration.setSupportLiveness(true);
        functionConfiguration.setSupportIRLiveness(true);
        engineConfiguration.setFunctionConfiguration(functionConfiguration);

        return engineConfiguration;
    }


    /**
     * 获取特征码
     */
    public String getFaceCode(ImageInfo imageInfo) throws IOException {

        // 1. 检测人脸
        ArrayList<FaceInfo> faceInfos = new ArrayList<>();
        int i = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfos);
        if (i != ErrorInfo.MOK.getValue()) {
            log.error("人脸特征提取出错 : 错误码：{}", i);
        }
        // 2. 特征提取
        FaceFeature faceFeature = new FaceFeature();
        int code = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfos.get(0), faceFeature);
        log.info("人脸特征提取结果：{}", code);
        byte[] featureData = faceFeature.getFeatureData();
        return Base64.encode(featureData);

    }

    /**
     * 检查图像中是否包含人脸
     *
     * @param imageInfo 图像信息
     * @return 是否包含人脸
     */
    public boolean checkIsPortrait(ImageInfo imageInfo) {
        if (imageInfo == null) return false;
        List<FaceInfo> faceInfoList = new ArrayList<>();
        try {
            faceEngine.detectFaces(
                    imageInfo.getImageData(),
                    imageInfo.getWidth(),
                    imageInfo.getHeight(),
                    ImageFormat.CP_PAF_BGR24, faceInfoList
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return !faceInfoList.isEmpty();
    }

    /**
     * 检查字节数组中是否包含人脸
     *
     * @param imageData 图像数据
     * @return 是否包含人脸
     */
    public boolean checkIsPortrait(byte[] imageData) {
        return this.checkIsPortrait(ImageFactory.getRGBData(imageData));
    }

    /**
     * 检查MultipartFile中是否包含人脸
     *
     * @param multipartFile 文件
     * @return 是否包含人脸
     */
    public boolean checkIsPortrait(MultipartFile multipartFile) {
        try {
            return this.checkIsPortrait(ImageFactory.getRGBData(multipartFile.getBytes()));
        } catch (Exception e) {
            log.error("检查MultipartFile中是否包含人脸出错 : {}", e.getMessage(), e);
        }
        return false;
    }

    /**
     * 检查文件中是否包含人脸
     *
     * @param file 文件
     * @return 是否包含人脸
     */
    public boolean checkIsPortrait(File file) {
        return this.checkIsPortrait(ImageFactory.getRGBData(file));
    }

    /**
     * 检测图像中的人脸
     *
     * @param imageData   图像数据
     * @param width       图像宽度
     * @param height      图像高度
     * @param imageFormat 图像格式
     * @return 包含人脸信息的列表
     */
    public ArrayList<FaceInfo> detectFaces(byte[] imageData, int width, int height, ImageFormat imageFormat) {
        ArrayList<FaceInfo> faceInfos = new ArrayList<>();
        int detectFacesCode = faceEngine.detectFaces(imageData, width, height, imageFormat, faceInfos);
        if (detectFacesCode != ErrorInfo.MOK.getValue()) {
            log.error("人脸检测出错 : {}", detectFacesCode);
        }
        return faceInfos;
    }

    /**
     * 提取人脸特征
     *
     * @param imageData   图像数据
     * @param width       图像宽度
     * @param height      图像高度
     * @param imageFormat 图像格式
     * @param faceInfo    人脸信息
     * @return 人脸特征
     */
    public FaceFeature extractFaceFeature(byte[] imageData, int width, int height, ImageFormat imageFormat, FaceInfo faceInfo) {
        FaceFeature faceFeature = new FaceFeature();
        int i = faceEngine.extractFaceFeature(imageData, width, height, imageFormat, faceInfo, faceFeature);
        if (i != ErrorInfo.MOK.getValue()) {
            log.error("人脸特征提取出错 : {}", i);
        }
        return faceFeature;
    }

    /**
     * IR 活体检测
     *
     * @param imageData   图像数据
     * @param width       图像宽度
     * @param height      图像高度
     * @param imageFormat 图像格式
     * @param faceInfo    人脸信息
     */
    public void detectIRLiveness(byte[] imageData, int width, int height, ImageFormat imageFormat, ArrayList<FaceInfo> faceInfo) {
        int errorCode = faceEngine.processIr(imageData, width, height, imageFormat, faceInfo, new FunctionConfiguration() {{
            setSupportIRLiveness(true);
        }});
        if (errorCode != ErrorInfo.MOK.getValue()) {
            log.error("IR 活体检测失败");
            throw new RuntimeException("IR 活体检测失败");
        }
    }


    /**
     * 人脸特征比对
     *
     * @param targetFaceFeatureImage 目标人脸图片
     * @param sourceFaceFeatureImage 源人脸图片
     * @param grayData               灰度图
     * @return true:相似 false:不相似
     */
    public boolean compare(ImageInfo targetFaceFeatureImage, ImageInfo sourceFaceFeatureImage, ImageInfo grayData) {
        byte[] targetFaceFeatureImageData = targetFaceFeatureImage.getImageData();
        byte[] sourceFaceFeatureImageData = sourceFaceFeatureImage.getImageData();
        int targetFaceFeatureWidth = targetFaceFeatureImage.getWidth();
        int targetFaceFeatureHeight = targetFaceFeatureImage.getHeight();
        int sourceFaceFeatureWidth = sourceFaceFeatureImage.getWidth();
        int sourceFaceFeatureHeight = sourceFaceFeatureImage.getHeight();
        ImageFormat targetFaceFeatureImageFormat = targetFaceFeatureImage.getImageFormat();
        ImageFormat sourceFaceFeatureImageFormat = sourceFaceFeatureImage.getImageFormat();

        // 检测目标人脸图片
        ArrayList<FaceInfo> targetFaceFeatureFaceInfos = detectFaces(targetFaceFeatureImageData, targetFaceFeatureWidth, targetFaceFeatureHeight, targetFaceFeatureImageFormat);
        if (targetFaceFeatureFaceInfos.isEmpty()) {
            log.error("人脸检测出错 : 请发送正确的人脸图片");
            throw new ServiceException("请发送正确的人脸图片");
        }

        // 使用 IR 活体检测目标人脸
        int processedIrCode = faceEngine.processIr(grayData.getImageData(), grayData.getWidth(), grayData.getHeight(), grayData.getImageFormat(), targetFaceFeatureFaceInfos, new FunctionConfiguration() {{
            setSupportIRLiveness(true);
        }});
        if (processedIrCode != ErrorInfo.MOK.getValue()) {
            log.error("活体检测出错 : 错误码：{}", processedIrCode);
            throw new ServiceException("活体检测出错");
        }

        // 检测源人脸图片
        ArrayList<FaceInfo> sourceFaceFeatureFaceInfos = detectFaces(sourceFaceFeatureImageData, sourceFaceFeatureWidth, sourceFaceFeatureHeight, sourceFaceFeatureImageFormat);
        if (sourceFaceFeatureFaceInfos.isEmpty()) {
            log.error("人脸检测出错 : 请发送正确的身份证图片");
            throw new ServiceException("请发送正确的身份证图片");
        }

        // 提取目标人脸特征
        FaceFeature targetFaceFeature = extractFaceFeature(targetFaceFeatureImageData, targetFaceFeatureWidth, targetFaceFeatureHeight, targetFaceFeatureImageFormat, targetFaceFeatureFaceInfos.get(0));
        if (targetFaceFeature == null) {
            log.error("人脸特征提取出错 : 请发送正确的人脸图片");
            throw new ServiceException("人脸特征提取出错");
        }

        // 提取源人脸特征
        FaceFeature sourceFaceFeature = extractFaceFeature(sourceFaceFeatureImageData, sourceFaceFeatureWidth, sourceFaceFeatureHeight, sourceFaceFeatureImageFormat, sourceFaceFeatureFaceInfos.get(0));
        if (sourceFaceFeature == null) {
            log.error("人脸特征提取出错 : 请发送正确的身份证图片");
            throw new ServiceException("人脸特征提取出错");
        }
        byte[] featureData = sourceFaceFeature.getFeatureData();
        // 人脸比对
        FaceSimilar faceSimilar = new FaceSimilar();
        int i = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
        if (i != ErrorInfo.MOK.getValue()) {
            log.error("人脸比对出错 : {}", i);
            throw new ServiceException("人脸比对出错");
        } else {
            log.info("人脸比对结果 : {}", faceSimilar.getScore());
        }
        return faceSimilar.getScore() >= 0.8f;
    }


    /**
     * 校验两张人脸的特征数据
     *
     * @param targetFeature 目标数据,人脸特征
     * @param sourceFeature 源数据 ， 人脸特征
     * @return true 代表同一个人  false 不同的人
     */
    public boolean compare(FaceFeature targetFeature, FaceFeature sourceFeature) {
        FaceSimilar faceSimilar = new FaceSimilar();
        int i = faceEngine.compareFaceFeature(targetFeature, sourceFeature, faceSimilar);
        if (i != ErrorInfo.MOK.getValue()) {
            log.error("人脸比对出错 : {}", i);
            throw new ServiceException("人脸比对出错");
        } else {
            log.info("人脸比对结果 : {}", faceSimilar.getScore());
        }
        return faceSimilar.getScore() >= 0.8f;
    }


    /**
     * 人脸特征比对
     *
     * @param targetFaceFeatureImage 目标人脸图片
     * @param sourceFaceFeatureImage 源人脸图片
     * @return true:相似 false:不相似
     */
    public boolean compare(ImageInfo targetFaceFeatureImage, ImageInfo sourceFaceFeatureImage) {
        byte[] targetFaceFeatureImageData = targetFaceFeatureImage.getImageData();
        byte[] sourceFaceFeatureImageData = sourceFaceFeatureImage.getImageData();
        int targetFaceFeatureWidth = targetFaceFeatureImage.getWidth();
        int targetFaceFeatureHeight = targetFaceFeatureImage.getHeight();
        int sourceFaceFeatureWidth = sourceFaceFeatureImage.getWidth();
        int sourceFaceFeatureHeight = sourceFaceFeatureImage.getHeight();
        ImageFormat targetFaceFeatureImageFormat = targetFaceFeatureImage.getImageFormat();
        ImageFormat sourceFaceFeatureImageFormat = sourceFaceFeatureImage.getImageFormat();

        // 检测目标人脸图片
        ArrayList<FaceInfo> targetFaceFeatureFaceInfos = detectFaces(targetFaceFeatureImageData, targetFaceFeatureWidth, targetFaceFeatureHeight, targetFaceFeatureImageFormat);
        if (targetFaceFeatureFaceInfos.isEmpty()) {
            log.error("人脸检测出错 : 请发送正确的人脸图片");
            throw new ServiceException("请发送正确的人脸图片");
        }

        // 检测源人脸图片
        ArrayList<FaceInfo> sourceFaceFeatureFaceInfos = detectFaces(sourceFaceFeatureImageData, sourceFaceFeatureWidth, sourceFaceFeatureHeight, sourceFaceFeatureImageFormat);
        if (sourceFaceFeatureFaceInfos.isEmpty()) {
            log.error("人脸检测出错 : 请发送正确的身份证图片");
            throw new ServiceException("请发送正确的身份证图片");
        }

        // 提取目标人脸特征
        FaceFeature targetFaceFeature = extractFaceFeature(targetFaceFeatureImageData, targetFaceFeatureWidth, targetFaceFeatureHeight, targetFaceFeatureImageFormat, targetFaceFeatureFaceInfos.get(0));
        if (targetFaceFeature == null) {
            log.error("人脸特征提取出错 : 请发送正确的人脸图片");
            throw new ServiceException("人脸特征提取出错");
        }

        // 提取源人脸特征
        FaceFeature sourceFaceFeature = extractFaceFeature(sourceFaceFeatureImageData, sourceFaceFeatureWidth, sourceFaceFeatureHeight, sourceFaceFeatureImageFormat, sourceFaceFeatureFaceInfos.get(0));
        if (sourceFaceFeature == null) {
            log.error("人脸特征提取出错 : 请发送正确的身份证图片");
            throw new ServiceException("人脸特征提取出错");
        }

        // 人脸比对
        FaceSimilar faceSimilar = new FaceSimilar();
        int i = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
        if (i != ErrorInfo.MOK.getValue()) {
            log.error("人脸比对出错 : {}", i);
            throw new ServiceException("人脸比对出错");
        } else {
            log.info("人脸比对结果 : {}", faceSimilar.getScore());
        }
        return faceSimilar.getScore() >= 0.8f;
    }
}
